#include "Common.h"
#include "Defines.h"
#include "PatternInterpolation.h"
#include <stdio.h>
#include <string.h>

// 6 points
//const unsigned pattern_mask = 0x3F;
//const unsigned pattern_array_size = 92;

// 10 points
//const unsigned pattern_mask = 0x60C3F;
//const unsigned pattern_array_size = 120;

// 12 points
const unsigned pattern_mask = 0x64CBF;
const unsigned pattern_array_size = 120;

// 20 points
//const unsigned pattern_mask = 0xFFFFF;
//const unsigned pattern_array_size = 120;

//const int REF_PIXEL_MAP_MATRIX[REF_PIXEL_NUMBER][2] = {
//	{-3, -2}, {-1, -2}, { 1, -2}, { 3, -2},
//	{ 3, -1}, { 3,  0}, { 3,  1}, { 3,  2},
//	{ 1,  2}, {-1,  2}, {-3,  2}, {-3,  1},
//	{-3,  0}, {-3, -1}, {-1, -1}, { 1, -1},
//	{ 1,  0}, { 1,  1}, {-1,  1}, {-1,  0} 
//};

extern unsigned int PATTERN_MATRIX[];
char COFF_MATRIX[][20];

bool use_6tap_for_unpattern = true;

imgpel InterpolatePixelWithPattern(imgpel ** full_rec, int width, int height, int i, int j, int mb_x, int mb_y )
{
	int x = mb_x * width + i;
	int y = mb_y * height + j;

	unsigned char ref_pixel_array[REF_PIXEL_NUMBER];
	unsigned char result;

	if ( x < HORIZONTAL_SKIP_COUNT )
	{
		// the very left edge of the entire frame

		if ( !use_6tap_for_unpattern )
		{
			// x should be 1
			ref_pixel_array[0] = ref_pixel_array[1] = (unsigned char)full_rec[y][x-1];
			ref_pixel_array[2] = (unsigned char) full_rec[y][x+1];
			ref_pixel_array[3] = (unsigned char) full_rec[y][x+3];
			result = Compute4TapInterpolation( ref_pixel_array );
			// printf( "LEFT EDGE: X: %03d, Y: %03d", x, y );
		}
		else
		{
			if ( x == 1 )
			{
				ref_pixel_array[0] = ref_pixel_array[1] = ref_pixel_array[2] = (unsigned char)full_rec[y][x-1];
				ref_pixel_array[3] = (unsigned char) full_rec[y][x+1];
				ref_pixel_array[4] = (unsigned char) full_rec[y][x+3];
				ref_pixel_array[5] = (unsigned char) full_rec[y][x+5];
				result = Compute6TapInterpolation( ref_pixel_array );
			}
			else
			{
				// x == 3
				ref_pixel_array[0] = ref_pixel_array[1] = (unsigned char)full_rec[y][x-3];
				ref_pixel_array[2] = (unsigned char) full_rec[y][x-1];
				ref_pixel_array[3] = (unsigned char) full_rec[y][x+1];
				ref_pixel_array[4] = (unsigned char) full_rec[y][x+3];
				ref_pixel_array[5] = (unsigned char) full_rec[y][x+5];
				result = Compute6TapInterpolation( ref_pixel_array );
			}

		}
	}
	else if ( i >= width - HORIZONTAL_SKIP_COUNT )
	{
		// printf( "RIGHT EDGE: X: %03d, Y: %03d", x, y );
		// the right edge of the macro block

		if ( !use_6tap_for_unpattern )
		{
			if ( i == 13 )
			{
				ref_pixel_array[0] = (unsigned char) full_rec[y][x-3];
				ref_pixel_array[1] = (unsigned char) full_rec[y][x-1];
				ref_pixel_array[2] = ref_pixel_array[3] = (unsigned char) full_rec[y][x+1];
				result = Compute4TapInterpolation( ref_pixel_array );
			}
			else
			{
				// i = 15
				ref_pixel_array[0] = (unsigned char) full_rec[y][x-3];
				ref_pixel_array[1] = (unsigned char) full_rec[y][x-1];
				ref_pixel_array[2] = ref_pixel_array[3] = (unsigned char) full_rec[y][x-1];
				result = Compute4TapInterpolation( ref_pixel_array );
			}
		}
		else
		{
			if ( i == 11 )
			{
				ref_pixel_array[0] = (unsigned char) full_rec[y][x-5];
				ref_pixel_array[1] = (unsigned char) full_rec[y][x-3];
				ref_pixel_array[2] = (unsigned char) full_rec[y][x-1];
				ref_pixel_array[3] = (unsigned char) full_rec[y][x+1];
				ref_pixel_array[4] = ref_pixel_array[5] = (unsigned char) full_rec[y][x+3];
				result = Compute6TapInterpolation( ref_pixel_array );
			}
			else if ( i == 13 )
			{
				ref_pixel_array[0] = (unsigned char) full_rec[y][x-5];
				ref_pixel_array[1] = (unsigned char) full_rec[y][x-3];
				ref_pixel_array[2] = (unsigned char) full_rec[y][x-1];
				ref_pixel_array[3] = ref_pixel_array[4] = ref_pixel_array[5] = (unsigned char) full_rec[y][x+1];
				result = Compute6TapInterpolation( ref_pixel_array );
			}
			else
			{
				// i = 15
				ref_pixel_array[0] = (unsigned char) full_rec[y][x-5];
				ref_pixel_array[1] = (unsigned char) full_rec[y][x-3];
				ref_pixel_array[2] = (unsigned char) full_rec[y][x-1];
				ref_pixel_array[3] = ref_pixel_array[4] = ref_pixel_array[5] = (unsigned char) full_rec[y][x-1];
				result = Compute6TapInterpolation( ref_pixel_array );
			}
		}
	}
	else if ( y < VERTICAL_SKIP_COUNT || j >= height - VERTICAL_SKIP_COUNT )
	{
		// the top line of the entire frame
		if ( ! use_6tap_for_unpattern )
		{
			// printf( "TOP BOTTOM: X: %03d, Y: %03d", x, y );
			Get4TapRefPixelArray( x, y, ref_pixel_array, full_rec );
			result = Compute4TapInterpolation( ref_pixel_array );
		}
		else
		{
			ref_pixel_array[0] = (unsigned char) full_rec[y][x-5];
			ref_pixel_array[1] = (unsigned char) full_rec[y][x-3];
			ref_pixel_array[2] = (unsigned char) full_rec[y][x-1];
			ref_pixel_array[3] = (unsigned char) full_rec[y][x+1];
			ref_pixel_array[4] = (unsigned char) full_rec[y][x+3];
			ref_pixel_array[5] = (unsigned char) full_rec[y][x+5];
			result = Compute6TapInterpolation( ref_pixel_array );
		}
	}
	else
	{
		// printf( "INNER AREA: X: %03d, Y: %03d", x, y );
		// the inner area
		GetRefPixelArray( x, y, ref_pixel_array, full_rec );

		// Check if flat area
		if ( CheckAreaFlat( ref_pixel_array ) )
		{
			// flat area
			if ( !use_6tap_for_unpattern )
			{
				Get4TapRefPixelArray( x, y, ref_pixel_array, full_rec );
				result = Compute4TapInterpolation( ref_pixel_array );
				
				// printf( "FLAT: X: %03d, Y: %03d", x, y );
			}
			else
			{
				ref_pixel_array[0] = (unsigned char) full_rec[y][x-5];
				ref_pixel_array[1] = (unsigned char) full_rec[y][x-3];
				ref_pixel_array[2] = (unsigned char) full_rec[y][x-1];
				ref_pixel_array[3] = (unsigned char) full_rec[y][x+1];
				ref_pixel_array[4] = (unsigned char) full_rec[y][x+3];
				ref_pixel_array[5] = (unsigned char) full_rec[y][x+5];
				result = Compute6TapInterpolation( ref_pixel_array );
			}
		}
		else
		{
			// printf( "UNFT: X: %03d, Y: %03d", x, y );
			// unflat area, use patterns
			char pattern_array[REF_PIXEL_NUMBER];
			BinarizeRefPixelArray( pattern_array, ref_pixel_array );

			int pattern = PatternArrayToInt( pattern_array );
			pattern &= pattern_mask;
			pattern = NormalizePatternForm( pattern );

			bool find = false;
			int pattern_index = 0;

			for( int i=0; i<pattern_array_size; i++ )
			{
				if ( PATTERN_MATRIX[i] == pattern )
				{
					find = true;
					pattern_index = i;
					break;
				}
			}

			if ( find )
			{
				// find the match
				result = ComputeInterpolationPixel( ref_pixel_array, COFF_MATRIX[pattern_index] );
				// printf( "UNFT: X: %03d, Y: %03d INDEX: %03d", x, y, pattern_index );
			}
			else
			{
				// no match, use 4tap
				// printf( "UNFT: X: %03d, Y: %03d UNPATTERN", x, y );
				if ( !use_6tap_for_unpattern )
				{
					Get4TapRefPixelArray( x, y, ref_pixel_array, full_rec );
					result = Compute4TapInterpolation( ref_pixel_array );
					
					// printf( "FLAT: X: %03d, Y: %03d", x, y );
				}
				else
				{
					ref_pixel_array[0] = (unsigned char) full_rec[y][x-5];
					ref_pixel_array[1] = (unsigned char) full_rec[y][x-3];
					ref_pixel_array[2] = (unsigned char) full_rec[y][x-1];
					ref_pixel_array[3] = (unsigned char) full_rec[y][x+1];
					ref_pixel_array[4] = (unsigned char) full_rec[y][x+3];
					ref_pixel_array[5] = (unsigned char) full_rec[y][x+5];
					result = Compute6TapInterpolation( ref_pixel_array );
				}
			}
		}
	}

	return result;

}



//unsigned int PatternArrayToInt( char * pattern_array )
//{
//	unsigned int pattern = 0;
//	for ( int i=0; i<REF_PIXEL_NUMBER; i++ )
//	{
//		pattern += pattern_array[REF_PIXEL_NUMBER-i-1] * (1<<i);
//	}
//
//	return pattern;
//}

//bool PatternIntToArray( unsigned int pattern, char * pattern_array )
//{
//	for ( int i=0; i<REF_PIXEL_NUMBER; i++ )
//	{
//		pattern_array[REF_PIXEL_NUMBER-i-1] = (pattern&(1<<i)) == 0? 0:1;
//	}
//
//	return true;
//}


bool GetRefPixelArray( int x, int y, unsigned char * ref_pixel_array,  imgpel ** frame )
{

	for( int i=0; i<REF_PIXEL_NUMBER; i++ )
	{
		ref_pixel_array[i] = frame[ (y+REF_PIXEL_MAP_MATRIX[i][1]) ][ x+REF_PIXEL_MAP_MATRIX[i][0] ];
	}

	return true;
}

bool Get4TapRefPixelArray( int x, int y, unsigned char * ref_pixel_array, imgpel ** frame )
{

	for( int i=0; i<4; i++ )
	{
		ref_pixel_array[i] = frame[y][x+REF_4TAP_PIXEL_MATRIX[i]];
	}

	return true;
}

//unsigned char ComputeInterpolationPixel( unsigned char * ref_pixel_array, char * coff_array )
//{
//	int result = 0;
//	int factor = 0;
//	for ( int i=0; i<REF_PIXEL_NUMBER; i++ )
//	{
//		result += coff_array[i] * ref_pixel_array[i];
//		factor += coff_array[i];
//	}
//
//	result = (result+factor/2) / factor;
//
//	if ( result < 0 ) result = 0;
//	if ( result > 255 ) result = 255;
//
//	return static_cast<unsigned char>( result );
//}

unsigned char Compute4TapInterpolation( unsigned char * ref_pixel_array )
{
	int result = 0;
	for ( int i=0; i<4; i++ )
	{
		result += REF_4TAP_COFF[i] * ref_pixel_array[i];
	}

	result += REF_4TAP_COFF_SUM/2;
	result /= REF_4TAP_COFF_SUM;

	if ( result < 0 ) result = 0;
	if ( result > 255 ) result = 255;

	return static_cast<unsigned char>( result );
}

unsigned char Compute6TapInterpolation( unsigned char * ref_pixel_array )
{
	int result = 0;
	for ( int i=0; i<6; i++ )
	{
		result += REF_6TAP_COFF[i] * ref_pixel_array[i];
	}

	result += REF_6TAP_COFF_SUM/2;
	result /= REF_6TAP_COFF_SUM;

	if ( result < 0 ) result = 0;
	if ( result > 255 ) result = 255;

	return static_cast<unsigned char>( result );
}

unsigned int NormalizePatternForm( unsigned int pattern )
{
	if ( (pattern & NORMAL_TEST_BIT) == 0 )
	{
		return pattern;
	}
	else
	{
		return (~pattern) & PATTERN_VALID_BITMASK;
	}
}


//unsigned int GetPatternBit( unsigned int pattern, int i )
//{
//	return pattern & (1<<(REF_PIXEL_NUMBER-1-i));
//}

//bool CheckAreaFlat( unsigned char * ref_pixel_array )
//{
//	// TODO: mask?
//	int max = 0;
//	int min = 256;
//
//	for ( int i=0; i<REF_PIXEL_NUMBER; i++ )
//	{
//		if ( max < ref_pixel_array[i] ) max = ref_pixel_array[i];
//		if ( min > ref_pixel_array[i] ) min = ref_pixel_array[i];
//	}
//
//	if ( max - min < FLAT_THRESHOLD_VALUE ) 
//	{
//		return true;
//	}
//	else
//	{
//		return false;
//	}
//}


//bool BinarizeRefPixelArray( char * pattern_array, unsigned char * ref_pixel_array )
//{
//	int sum = 0;
//	int count = 0;
//	for ( int i=0; i<REF_PIXEL_NUMBER; i++ )
//	{
//		if ( GetPatternBit( pattern_mask, i ) != 0 )
//		{
//			sum += ref_pixel_array[i];
//			count++;
//		}
//	}
//	sum = sum/count;
//
//	for ( int i=0; i<REF_PIXEL_NUMBER; i++ )
//	{
//		if ( GetPatternBit( pattern_mask, i ) != 0 )
//		{
//			pattern_array[i] = (ref_pixel_array[i] > sum)? 1:0;
//		}
//	}
//
//	return true;
//}